package site.isscloud.project.service.impl;

import org.springframework.stereotype.Service;
import site.isscloud.common.core.service.impl.BaseExcelImportService;
import site.isscloud.project.domain.PjLocation;
import site.isscloud.project.mapper.PjLocationMapper;
import site.isscloud.project.service.IPjLocationImportService;
import site.isscloud.common.utils.DateUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 空间管理导入服务实现类
 *
 * 负责空间管理数据的Excel导入功能，包括数据验证、关系处理和数据库操作。
 * 继承BaseExcelImportService基类，实现空间管理特定的导入逻辑。
 *
 * 主要功能：
 * 1. 空间数据的批量导入
 * 2. 空间层级关系的自动建立
 * 3. 数据唯一性校验
 * 4. 父子空间关系的编码转换
 *
 * @author junhuang
 * @date 2025-10-14
 */
@Service
public class PjLocationImportService extends BaseExcelImportService<PjLocation> implements IPjLocationImportService {

    @Resource
    private PjLocationMapper pjLocationMapper;

    /**
     * 加载数据库中已存在的空间数据
     *
     * 构建空间编码到空间ID的映射关系，用于快速查找和去重。
     * 映射结构：key=空间编码(lcCode), value=空间ID(lcId)
     *
     * @return 现有空间数据的映射表，包含所有已存在空间的编码和ID对应关系
     */
    @Override
    protected Map<String, Object> loadExistingData() {
        Map<String, Object> existingData = new HashMap<>();
        // 查询数据库中所有空间数据
        List<PjLocation> allLocations = pjLocationMapper.selectPjLocationList(new PjLocation());
        for (PjLocation location : allLocations) {
            // 建立编码到ID的映射，用于后续数据存在性检查
            existingData.put(location.getLcCode(), location.getLcId());
        }
        return existingData;
    }

    /**
     * 获取空间数据的唯一标识键
     *
     * 使用空间编码作为数据的唯一标识，用于判断数据是否重复。
     * 空间编码在系统中具有唯一性约束。
     *
     * @param data 空间数据对象
     * @return 空间编码的trimmed字符串
     */
    @Override
    protected String getUniqueKey(PjLocation data) {
        return data.getLcCode().trim();
    }

    /**
     * 获取空间数据的显示名称
     *
     * 用于在导入结果消息中显示，方便用户识别具体的空间数据。
     *
     * @param data 空间数据对象
     * @return 格式化的空间显示名称，如"空间 石桥花园A区2栋1单元"
     */
    @Override
    protected String getDataName(PjLocation data) {
        return "空间 " + data.getLcName();
    }

    /**
     * 获取空间数据的主键ID
     *
     * 用于记录已处理数据的ID，便于后续数据建立父子关系。
     * 在插入新数据后，会自动获取数据库生成的主键ID。
     *
     * @param data 空间数据对象
     * @return 空间ID
     */
    @Override
    protected Object getDataId(PjLocation data) {
        return data.getLcId();
    }

    /**
     * 检查空间数据是否已存在
     *
     * 检查空间数据是否已存在于数据库或本次导入的已处理数据中。
     * 检查范围包括：
     * 1. 数据库中已存在的空间数据
     * 2. 本次导入过程中已成功处理的空间数据
     *
     * @param uniqueKey 空间编码（唯一标识）
     * @param existingData 数据库中已存在的数据映射
     * @param processedData 本次导入已处理的数据映射
     * @return true-数据已存在，false-数据不存在
     */
    @Override
    protected boolean isDataExists(String uniqueKey, Map<String, Object> existingData, Map<String, Object> processedData) {
        return existingData.containsKey(uniqueKey) || processedData.containsKey(uniqueKey);
    }

    /**
     * 插入新的空间数据
     *
     * 执行空间数据的数据库插入操作，同时设置创建时间戳。
     * 插入成功后，数据的主键ID会自动回填到对象中。
     *
     * @param data 要插入的空间数据对象
     */
    @Override
    protected void insertData(PjLocation data) {
        // 设置创建时间
        data.setCreateTime(DateUtils.getNowDate());
        // 调用Mapper执行数据库插入
        pjLocationMapper.insertPjLocation(data);
    }

    /**
     * 更新已存在的空间数据
     *
     * 执行空间数据的数据库更新操作，同时设置更新时间戳。
     * 仅当开启更新支持(updateSupport=true)时才会调用此方法。
     *
     * @param data 要更新的空间数据对象
     */
    @Override
    protected void updateData(PjLocation data) {
        // 设置更新时间
        data.setUpdateTime(DateUtils.getNowDate());
        // 调用Mapper执行数据库更新
        pjLocationMapper.updatePjLocation(data);
    }

    /**
     * 验证空间数据的合法性
     *
     * 对导入的空间数据进行业务规则验证，确保数据的完整性。
     * 验证规则：
     * 1. 空间编码不能为空
     * 2. 空间名称不能为空
     *
     * @param data 要验证的空间数据对象
     * @return 验证错误信息，返回null表示验证通过
     */
    @Override
    public String validateData(PjLocation data) {
        // 验证空间编码必填
        if (data.getLcCode() == null || data.getLcCode().trim().isEmpty()) {
            return "空间编码不能为空";
        }
        // 验证空间名称必填
        if (data.getLcName() == null || data.getLcName().trim().isEmpty()) {
            return "空间名称不能为空";
        }
        // 验证通过
        return null;
    }

    /**
     * 处理空间数据的关联关系
     *
     * 主要处理空间数据的层级关系，将父空间编码转换为父空间ID。
     * 处理逻辑：
     * 1. 如果父空间编码为空，设置为根节点(lcPid=0)
     * 2. 如果父空间编码不为空，查找对应的父空间ID
     * 3. 设置空间状态的默认值
     *
     * @param data 当前处理的空间数据对象
     * @param processedData 本次导入已处理的数据映射
     * @param existingData 数据库中已存在的数据映射
     */
    @Override
    public void processDataRelations(PjLocation data, Map<String, Object> processedData, Map<String, Object> existingData) {
        // 获取并清理父空间编码
        String parentCode = data.getParentCode() != null ? data.getParentCode().trim() : null;

        if (parentCode == null || parentCode.isEmpty()) {
            // 父空间编码为空，设置为根节点
            data.setLcPid(0L);
        } else {
            // 父空间编码不为空，查找对应的父空间ID
            Long parentId = findParentId(parentCode, existingData, processedData);
            data.setLcPid(parentId);
        }

        // 设置默认状态：如果状态为空，默认为"0"(正常状态)
        if (data.getStatus() == null || data.getStatus().trim().isEmpty()) {
            data.setStatus("0");
        }
    }

    /**
     * 查找父空间ID
     *
     * 根据父空间编码查找对应的父空间ID。
     * 查找顺序：
     * 1. 首先在数据库已存在数据中查找
     * 2. 然后在本次导入已处理数据中查找
     * 3. 如果都找不到，抛出异常
     *
     * 这种查找顺序支持同一Excel文件中父子空间的交叉引用。
     *
     * @param parentCode 父空间编码
     * @param existingData 数据库中已存在的数据映射
     * @param processedData 本次导入已处理的数据映射
     * @return 父空间ID
     * @throws RuntimeException 当父空间编码不存在时抛出
     */
    private Long findParentId(String parentCode, Map<String, Object> existingData, Map<String, Object> processedData) {
        // 首先在已存在数据中查找（数据库中的现有数据）
        Long parentId = (Long) existingData.get(parentCode);
        if (parentId != null) {
            return parentId;
        }

        // 然后在已处理数据中查找（本次导入已成功插入的数据）
        parentId = (Long) processedData.get(parentCode);
        if (parentId != null) {
            return parentId;
        }

        // 父空间编码不存在，抛出异常
        throw new RuntimeException("父空间编码 " + parentCode + " 不存在");
    }
}